import {
  AmbientLight,
  AxesHelper,
  BoxGeometry,
  DirectionalLight,
  GridHelper,
  Group,
  HemisphereLight,
  Mesh,
  MeshBasicMaterial,
  MeshPhysicalMaterial,
  MeshStandardMaterial,
  PerspectiveCamera,
  Raycaster,
  Scene,
  SphereGeometry,
  WebGLRenderer,
} from "three";
import { OrbitControls } from "three/examples/jsm/Addons.js";
import GUI from "three/examples/jsm/libs/lil-gui.module.min.js";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { Vector2 } from "three";

export default class Y3DEngine {
  constructor(options = {}) {
    this.options = options;
    this.init();
    this.animate();
  }

  init() {
    this.initScene();
    this.initCamera();
    this.initRenderer();
    this.initLight();
    this.initObject();
    this.initHelper();
    this.initControls();
    this.initStats();
    this.initGUI();
    this.initEvent();
    this.initRaycaster();
  }

  get height() {
    return window.innerHeight;
  }

  get width() {
    return window.innerWidth;
  }

  get aspect() {
    return this.width / this.height;
  }

  initScene() {
    this.scene = new Scene();
  }

  initCamera() {
    this.camera = new PerspectiveCamera(75, this.aspect, 0.1, 10000);
    this.camera.position.set(0, 0, 5);
  }

  initRenderer() {
    const { width, height } = this;
    this.renderer = new WebGLRenderer({
      antialias: true,
      alpha: true,
    });
    this.renderer.setSize(width, height);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setClearColor(0x101010);
    document.body.appendChild(this.renderer.domElement);
  }

  initLight() {
    this.lights = new Group();
    this.scene.add(this.lights);
    this.lights.add(new AmbientLight(0xffffff, 1));
    const light = new DirectionalLight(0xffffff, 1);
    light.position.set(0, 0, 10).normalize();
    // this.lights.add(light);

    const hemiLight = new HemisphereLight(0xffffff, 0x444444);
    this.lights.add(hemiLight);
  }

  initObject() {
    this.objects = new Group();
    this.scene.add(this.objects);
    let box;
    const color = 0xffffff;

    //加一个盒子
    const geometry = new SphereGeometry(1);
    box = new Mesh(
      geometry,
      new MeshStandardMaterial({
        color, // 钢管的颜色，这里是中灰色
        roughness: 0.5, // 粗糙度，值越小越光滑
        metalness: 0.0, // 金属度，接近 1 表示像金属
      })
    );
    this.objects.add(box);

    //加一个盒子
    box = new Mesh(
      geometry,
      new MeshBasicMaterial({
        color, // 钢管的颜色，这里是中灰色
      })
    );
    box.position.set(3, 0, 0);
    this.objects.add(box);
  }

  initHelper() {
    this.helpers = new Group();
    this.scene.add(this.helpers);
    this.helpers.add(new AxesHelper(100));
    this.helpers.add(new GridHelper(10, 10));
  }

  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  }

  initStats() {
    this.stats = new Stats();
    document.body.appendChild(this.stats.dom);
  }

  initGUI() {
    this.gui = new GUI();
  }

  initEvent() {
    window.addEventListener("resize", this.onWindowResize);
    window.addEventListener("pointermove", this.onPointerMove);
    window.addEventListener("pointerup", this.onPointerUp);
  }

  initRaycaster() {
    this.raycaster = new Raycaster();
    this.pointer = new Vector2();
  }

  onPointerMove = (e) => {
    //真实的屏幕坐标
    const { clientX, clientY } = e;
    //屏幕坐标转NDC
    const x = (clientX / this.width) * 2 - 1;
    const y = -(clientY / this.height) * 2 + 1;
    this.pointer.set(x, y);
    this.raycaster.setFromCamera(this.pointer, this.camera);
    const intersects = this.raycaster.intersectObjects(this.objects.children);
    if (intersects.length > 0) {
      console.log(intersects[0]);
    }
  };

  onPointerUp = (e) => {
    this.raycaster.setFromCamera(this.pointer, this.camera);
    const intersects = this.raycaster.intersectObjects(this.objects.children);
    if (intersects.length > 0) {
      console.log(intersects[0]);
    }
  };

  onWindowResize = () => {
    const { width, height, aspect } = this;
    this.camera.aspect = aspect;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(width, height);
  };

  animate = () => {
    requestAnimationFrame(this.animate);
    this.render();
  };

  render = () => {
    this.renderer.render(this.scene, this.camera);
    this.stats.update();
  };
}

window.addEventListener("DOMContentLoaded", () => {
  const engine = new Y3DEngine();
  window.engine = engine;
});
